﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib;
using FFmpegVideoClip.Lib.TaskFlowProcessor;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;
using UtilityToolsCollect.ObjectsLibrary.TaskFlowProcessor;


namespace FFmpegVideoClip.Processing
{
    internal class ClipTaskProcessor
    {
        public static async Task ExecuteOut(MainWindow window, IEnumerable<Clip> clips, bool SplitFrameMode, ProgressBar StatusProgress)
        {
            if (clips.SelectMany(p => p.Fragments).Count() < 1)
            {
                MessageBoxEx.Show(window, $"没有片段信息", "操作错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            if (clips.Any(p => string.IsNullOrWhiteSpace(p.Name)))
            {
                MessageBoxEx.Show(window, $"剪辑有未命名项，请修正后重试!", "命名错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (clips.CheckDuplicate(p => p.Name, StringComparer.OrdinalIgnoreCase))
            {
                MessageBoxEx.Show(window, $"剪辑有重名项，请修正后重试!\r\n重名项:\r\n{string.Join("\r\n", ClipManage.ClipList.GroupBy(p => p.Name).Where(p => p.Count() > 1).Select(p => p.Key))}"
                    , "命名错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            TaskFlowProcessor taskProcessors = new TaskFlowProcessor(clips);
            Action<double, double> spc = (max, value) => window.Dispatcher.BeginInvoke(() => { StatusProgress.Maximum = max; StatusProgress.Value = value; });

            taskProcessors.Add(new SplitTaskFlow(spc, window));
            taskProcessors.Add(new MergeTaskFlow(spc, window));
            MainWindow.CloseStatusInfo();
            MainWindow.ShowStatusProgress();
            int Result = await taskProcessors.ExecuteAsync(SplitFrameMode);
            if (Result < 0)
                MainWindow.ShowStatusInfo("执行错误", Brushes.Red);
            else
                MainWindow.ShowStatusInfo("完成", Brushes.DarkSeaGreen);
            MainWindow.CloseStatusProgress();
        }

        public static async Task SingleClipMergeAsync(MainWindow window, Clip clip, ProgressBar StatusProgress)
        {
            LogManage.Clear();
            string Error;
            string TempCacheDirectory = clip.TempCacheDirectory;
            clip.ClearTempCheck();
            Tools.CheckDirectoryPath(clip.TempCacheDirectory, out Error, true);
            Tools.CheckDirectoryPath(clip.OutDirectory, out Error, true);
            string inTextFile = Path.Combine(TempCacheDirectory,"MergeList.txt");

            TimeSpan Duration = clip.TimeDuration;

            StatusProgress.Minimum = 0;
            StatusProgress.Maximum = Duration.TotalSeconds;

            Tools.CurrentCancelToken = new();

            string OutPath = Tools.CheckDuplicateNameAndLegal(Path.Combine(clip.OutDirectory, clip.Name + clip.Fragments.First().InputFileExtension));

            StringBuilder stringBuilder = new StringBuilder();
            foreach(string p in clip.Fragments.Select(p=>p.InputPath))
                stringBuilder.AppendLine($"file '{p}'");
            File.WriteAllText(inTextFile, stringBuilder.ToString());

            SimpleFFmpegProcessor FFconcat = new SimpleFFmpegProcessor("-c copy", $"\"{inTextFile}\"", OutPath, Tools.CurrentCancelToken, "-f concat -safe 0");

            MainWindow.CloseStatusInfo();
            MainWindow.ShowStatusProgress();

            _ = Task.Run(() =>
            {
                while (!Tools.IsCancellationRequested)
                {
                    StatusProgress.Dispatcher.BeginInvoke(() => { StatusProgress.Value = FFconcat.TimeProgress.TotalSeconds; });
                    Thread.Sleep(500);
                }
            });


            await Task.Run(() => FFconcat.Execute());

            if (Tools.IsCancellationRequested)
            {
                MainWindow.CloseStatusProgress();
                MainWindow.ShowStatusInfo("取消", Brushes.Red);
                Tools.CurrentCancelToken = null;
                return;
            }

            if (Config.AutoClearCache)
                Directory.Delete(TempCacheDirectory, true);

            MainWindow.CloseStatusProgress();
            MainWindow.ShowStatusInfo("完成", Brushes.DarkSeaGreen);
            Tools.CurrentCancelToken = null;
        }

        public static  async Task SingleClipTSMergeAsync(MainWindow window, Clip clip, ProgressBar StatusProgress)
        {
            LogManage.Clear();
            string Error;
            string TempCacheDirectory = clip.TempCacheDirectory;
            clip.ClearTempCheck();
            Tools.CheckDirectoryPath(clip.TempCacheDirectory, out Error, true);
            Tools.CheckDirectoryPath(clip.OutDirectory, out Error, true);

            TempFileData[] MergeData = clip.Fragments.Select(p => new TempFileData(p.InputPath, Path.Combine(TempCacheDirectory, $"{p.Name}.ts"))).ToArray();

            TimeSpan Duration = clip.TimeDuration * 2;
            double L1Time = 0d;

            StatusProgress.Minimum = 0;
            StatusProgress.Maximum = Duration.TotalSeconds;

            Tools.CurrentCancelToken = new();
            SimpleFFmpegProcessor[] TsProcessors = MergeData.Select(p => new SimpleFFmpegProcessor("-c copy", $"\"{p.InputPath}\"", p.OutPath, Tools.CurrentCancelToken)).ToArray();

            string OutPath = Tools.CheckDuplicateNameAndLegal(Path.Combine(clip.OutDirectory, clip.Name + clip.Fragments.First().InputFileExtension));
            string inPahts = string.Join('|', MergeData.Select(p => p.OutPath));
            SimpleFFmpegProcessor FFconcat = new SimpleFFmpegProcessor("-c copy", $"concat:\"{inPahts}\"", OutPath, Tools.CurrentCancelToken);

            MainWindow.CloseStatusInfo();
            MainWindow.ShowStatusProgress();

            _ = Task.Run(() =>
            {
                while (!Tools.IsCancellationRequested)
                {
                    Interlocked.Exchange(ref L1Time, TsProcessors.Select(p => p.TimeProgress).Sum().TotalSeconds) ;
                    StatusProgress.Dispatcher.BeginInvoke(() => { StatusProgress.Value = L1Time + FFconcat.TimeProgress.TotalSeconds; });
                    Thread.Sleep(500);
                }
            });

            await Task.Run(() =>
            {
                try
                {
                    Parallel.ForEach(TsProcessors, new ParallelOptions { MaxDegreeOfParallelism = Config.MaxThread, TaskScheduler = TaskSchedulerSelector.Get, CancellationToken = Tools.CurrentCancelToken!.Token },
                    TsProcessor => { if (!Tools.IsCancellationRequested) TsProcessor.Execute(); });
                }
                catch(OperationCanceledException) {}
                catch (Exception ex) { LogManage.WriteExceptionInfo($"转换到TS异常: [clip:{clip.Name}]", ex, true); }
            });

            if (Tools.IsCancellationRequested)
            {
                MainWindow.CloseStatusProgress();
                MainWindow.ShowStatusInfo("取消", Brushes.Red);
                Tools.CurrentCancelToken = null;
                return;
            }

            foreach (TempFileData item in MergeData)
            {
                if (!File.Exists(item.OutPath))
                {
                    Tools.CurrentCancelToken?.Cancel();
                    Tools.CurrentCancelToken = null;
                    MainWindow.CloseStatusProgress();
                    MainWindow.ShowStatusInfo("错误", Brushes.Red);
                    return;
                }
            }

            Interlocked.Exchange(ref L1Time, Duration.TotalSeconds / 2);

            await Task.Run(() => FFconcat.Execute());

            if (Tools.IsCancellationRequested)
            {
                MainWindow.CloseStatusProgress();
                MainWindow.ShowStatusInfo("取消", Brushes.Red);
                Tools.CurrentCancelToken = null;
                return;
            }

            if (Config.AutoClearCache)
                Directory.Delete(TempCacheDirectory, true);

            MainWindow.CloseStatusProgress();
            MainWindow.ShowStatusInfo("完成", Brushes.DarkSeaGreen);
            Tools.CurrentCancelToken = null;
        }
    }

    internal record class TempFileData(string InputPath,string OutPath);

    internal class SimpleFFmpegProcessor
    {
        public static readonly Regex ProgressValueRegex = new(Config.MainCoreProcessor.GetProgressValueExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        //public static readonly Regex SuccessRegex = new(Config.MainCoreProcessor.SuccessValidatedExpression, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);

        private string PreArgs;
        private string args;
        private string inFilePath, outFilePath;
        protected ProcessShell? Process;

        public TimeSpan TimeProgress;

        public SimpleFFmpegProcessor(string args, string inFile, string OutFile, CancellationTokenSource cancellation, string PreArgs = "")
        {
            this.PreArgs = PreArgs;
            this.args = args; 
            inFilePath = inFile; 
            outFilePath = OutFile;
            cancellation.Token.Register(Cancel);
        }
        
        private void Cancel() { if (Process != null) { Process.Dispose(); Process = null; } }

        public void GetProcessor(string? value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return;
            
            LogManage.WriteLineNewInfo(value);

            Match match = ProgressValueRegex.Match(value);
            if (match.Success)
                TimeProgress = Tools.GetStringTimeToTimeSpan(match.Value);
        }

        public void Execute()
        {
            ProcessShell.ProcessExecuteWaitForExit(Config.CoreProcessor, $"-hide_banner {PreArgs} -i {inFilePath} {args} \"{outFilePath}\"", ErrorReceived: GetProcessor, BeforeExecute: p => Process = p);
        }
    }
}
